package com.example.persistence;

import com.alibaba.csp.sentinel.command.handler.ModifyParamFlowRulesCommandHandler;
import com.alibaba.csp.sentinel.datasource.*;
import com.alibaba.csp.sentinel.init.InitFunc;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRuleManager;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRuleManager;
import com.alibaba.csp.sentinel.slots.system.SystemRule;
import com.alibaba.csp.sentinel.slots.system.SystemRuleManager;
import com.alibaba.csp.sentinel.transport.util.WritableDataSourceRegistry;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;

import java.io.File;
import java.io.IOException;
import java.util.List;

/**
 * @Author 刘武贵
 * @Date 2024-03-04 22:41
 * @Description 功能描述：Sentinel规则持久化
 */
/**
 * 守护进程持久化规则类，实现InitFunc接口。
 * 该类用于定义和实现守护进程的持久化规则，确保守护进程在系统重启后仍能恢复到之前的状态。
 */
public class SentinelPersistenceRule implements InitFunc {

    /**
     * 应用名称 实际可以从外部配置读取
     */
    private String appcationName = "server-order";

    /**
     * 初始化函数，用于加载和注册各种规则（流控、降级、系统、授权、热点参数）到Sentinel中。
     * 它首先会根据应用名称创建规则文件目录，并确保目录存在，然后为每种规则创建对应的文件。
     * 接着，初始化并注册可读数据源（用于加载规则）和可写数据源（用于保存规则）到相应的规则管理器中。
     *
     * @throws Exception 如果在初始化过程中遇到任何错误，将会抛出异常。
     */
    @Override
    public void init() throws Exception {
        // 构造规则文件的路径
        // 获取用户主目录，并构建Sentinel规则文件的存储路径
//        String ruleDir = System.getProperty("E:\\study\\my\\shop-springcloud-alibaba-my\\doc\\Sentinel") + "/sentinel-rules/" + appcationName;
        String ruleDir = "E:\\study\\my\\shop-springcloud-alibaba-my\\doc\\Sentinel/sentinel-rules/" + appcationName;
        // 构建流控规则文件路径
        String flowRulePath = ruleDir + "/flow-rule.json";
        // 构建降级规则文件路径
        String degradeRulePath = ruleDir + "/degrade-rule.json";
        // 构建系统规则文件路径
        String systemRulePath = ruleDir + "/system-rule.json";
        // 构建权限规则文件路径
        String authorityRulePath = ruleDir + "/authority-rule.json";
        // 构建参数流控规则文件路径
        String paramFlowRulePath = ruleDir + "/param-flow-rule.json";

        // 确保规则目录存在，不存在则创建
        this.mkdirIfNotExits(ruleDir);
        // 创建各种规则的文件，如果不存在的话
        this.createFileIfNotExits(flowRulePath);
        this.createFileIfNotExits(degradeRulePath);
        this.createFileIfNotExits(systemRulePath);
        this.createFileIfNotExits(authorityRulePath);
        this.createFileIfNotExits(paramFlowRulePath);

        // 流控规则 初始化并注册流控规则的数据源
        ReadableDataSource<String, List<FlowRule>> flowRuleRDS = new FileRefreshableDataSource<>(
                flowRulePath,
                flowRuleListParser
        );
        // 将流控规则注册到规则管理器中，以便于系统可以使用这些规则进行流量控制
        FlowRuleManager.register2Property(flowRuleRDS.getProperty());

        // 创建一个可写的数据源，用于保存更新后的流控规则到文件
        WritableDataSource<List<FlowRule>> flowRuleWDS = new FileWritableDataSource<>(
                flowRulePath,
                this::encodeJson
        );
        // 将这个可写数据源注册到可写数据源注册中心，以便于系统能够通过API更新流控规则
        WritableDataSourceRegistry.registerFlowDataSource(flowRuleWDS);

        // 降级规则 初始化并注册降级规则的数据源
        ReadableDataSource<String, List<DegradeRule>> degradeRuleRDS = new FileRefreshableDataSource<>(
                degradeRulePath,
                degradeRuleListParser
        );
        // 将降级规则注册到降级规则管理器中，以便于系统可以使用这些规则进行服务降级处理
        DegradeRuleManager.register2Property(degradeRuleRDS.getProperty());

        // 创建一个可写的数据源，用于更新降级规则文件
        WritableDataSource<List<DegradeRule>> degradeRuleWDS = new FileWritableDataSource<>(
                // 指定降级规则文件的路径
                degradeRulePath,
                // 指定将降级规则列表转换为JSON字符串的方法
                this::encodeJson
        );
        // 将降级规则的更新数据源注册到可写数据源注册表中，以支持动态更新降级规则
        WritableDataSourceRegistry.registerDegradeDataSource(degradeRuleWDS);

        // 系统规则 初始化并注册系统规则的数据源
        ReadableDataSource<String, List<SystemRule>> systemRuleRDS = new FileRefreshableDataSource<>(
                systemRulePath,
                systemRuleListParser
        );
        // 将系统规则注册到系统属性中，以便可以在其他地方通过属性访问
        SystemRuleManager.register2Property(systemRuleRDS.getProperty());

        // 创建一个可写的数据源，用于保存系统规则列表
        WritableDataSource<List<SystemRule>> systemRuleWDS = new FileWritableDataSource<>(
                // 指定保存系统规则文件的路径
                systemRulePath,
                // 使用此对象的encodeJson方法来将系统规则列表编码为JSON格式
                this::encodeJson
        );
        // 将可写数据源注册为系统数据源，以便可以在其他地方通过系统数据源接口保存系统规则
        WritableDataSourceRegistry.registerSystemDataSource(systemRuleWDS);

        // 授权规则 初始化并注册授权规则的数据源
        ReadableDataSource<String, List<AuthorityRule>> authorityRuleRDS = new FileRefreshableDataSource<>(
                authorityRulePath,
                authorityRuleListParser
        );
        // 将权限规则注册到属性管理器中，以便可以在系统中访问这些规则
        AuthorityRuleManager.register2Property(authorityRuleRDS.getProperty());

        // 创建一个可写的数据源，用于保存权限规则到文件中
        WritableDataSource<List<AuthorityRule>> authorityRuleWDS = new FileWritableDataSource<>(
                // 权限规则文件的路径
                authorityRulePath,
                // 用于将权限规则列表编码为JSON格式的方法引用
                this::encodeJson
        );
        // 将权限规则数据源注册到可写数据源注册表中，以便系统可以保存更新的权限规则
        WritableDataSourceRegistry.registerAuthorityDataSource(authorityRuleWDS);

        // 热点参数规则 初始化并注册热点参数规则的数据源
        ReadableDataSource<String, List<ParamFlowRule>> paramFlowRuleRDS = new FileRefreshableDataSource<>(
                paramFlowRulePath,
                paramFlowRuleListParser
        );
        // 将参数流控规则注册到管理器中，以便于属性更新时自动加载
        ParamFlowRuleManager.register2Property(paramFlowRuleRDS.getProperty());

        // 创建一个可写的数据源，用于保存参数流控规则
        WritableDataSource<List<ParamFlowRule>> paramFlowRuleWDS = new FileWritableDataSource<>(
                // 规则文件路径
                paramFlowRulePath,
                // 保存时，使用该方法将规则列表编码为JSON格式
                this::encodeJson
        );
        // 设置可写数据源，用于处理参数流控规则的修改命令
        ModifyParamFlowRulesCommandHandler.setWritableDataSource(paramFlowRuleWDS);
    }

    /**
     * 定义一个转换器，用于将JSON字符串转换为FlowRule列表。
     *
     * @param source 输入的JSON字符串。
     * @return 转换后的FlowRule对象列表。
     */
    private Converter<String, List<FlowRule>> flowRuleListParser = source -> JSON.parseObject(
            source,
            new TypeReference<List<FlowRule>>() {
            }
    );

    /**
     * 声明一个转换器，用于将字符串转换为降级规则列表。
     * 这个转换器使用JSON解析器将输入的字符串解析为一个降级规则的列表。
     *
     * @param source 输入的字符串，预期为JSON格式，包含一个降级规则列表。
     * @return 返回一个降级规则的列表。降级规则是用于控制服务降级的规则集合。
     */
    private Converter<String, List<DegradeRule>> degradeRuleListParser = source -> JSON.parseObject(
            source,
            new TypeReference<List<DegradeRule>>() {
            }
    );

    /**
     * 定义一个字符串到系统规则列表的转换器。这个转换器使用JSON解析技术，将输入的字符串源数据解析成一个系统规则列表。
     *
     * @param source 输入的待解析字符串。
     * @return 解析后的系统规则列表。
     */
    private Converter<String, List<SystemRule>> systemRuleListParser = source -> JSON.parseObject(
            source,
            new TypeReference<List<SystemRule>>() {
            }
    );

    /**
     * 定义一个转换器，用于将字符串转换为权限规则列表。
     * 这个转换器使用JSON解析器将输入的字符串解析为一个权限规则对象列表。
     *
     * @param source 输入的字符串，预期为JSON格式，包含一组权限规则。
     * @return 返回一个解析后的权限规则列表。
     */
    private Converter<String, List<AuthorityRule>> authorityRuleListParser = source -> JSON.parseObject(
            source,
            new TypeReference<List<AuthorityRule>>() {
            }
    );

    /**
     * 参数流控规则列表解析器。
     * 该解析器用于将JSON格式的字符串转换为参数流控规则列表（List<ParamFlowRule>）。
     */
    private Converter<String, List<ParamFlowRule>> paramFlowRuleListParser = source -> JSON.parseObject(
            source,
            new TypeReference<List<ParamFlowRule>>() {
            }
    );

    /**
     * 如果指定的文件路径不存在，则创建该路径的所有父目录。
     *
     * @param filePath 要检查和创建的文件路径
     * @throws IOException 如果在创建目录时发生IO异常
     */
    private void mkdirIfNotExits(String filePath) throws IOException {
        File file = new File(filePath);
        // 检查文件路径是否存在，如果不存在，则创建所有父目录
        if (!file.exists()) {
            file.mkdirs();
        }
    }

    /**
     * 如果指定的文件不存在，则创建该文件。
     *
     * @param filePath 要检查和创建的文件的路径。
     * @throws IOException 如果在创建文件时发生IO异常。
     */
    private void createFileIfNotExits(String filePath) throws IOException {
        // 根据提供的路径创建File对象
        File file = new File(filePath);
        // 检查文件是否存在
        if (!file.exists()) {
            // 如果文件不存在，则创建新文件
            file.createNewFile();
        }
    }

    /**
     * 将对象转换为JSON字符串。
     *
     * @param t 需要转换为JSON字符串的对象。
     * @return 返回转换后的JSON字符串。
     * @param <T> 输入对象的类型。
     */
    private <T> String encodeJson(T t) {
        // 使用JSON库将对象转换为字符串
        return JSON.toJSONString(t);
    }
}

